package com.class2.java.address;

import com.class2.java.tools.Base58;
import com.class2.java.tools.Hash;

import java.security.*;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECPoint;
import java.util.Arrays;

/**
 * @author: yc
 * @email: 1247616535@qq.com
 * @desc: 该类用于推导生成比特币原始格式地址，并执行校验
 * @datetime: 2021/12/26 17:34
 */
public class Address {
    /**
     * 该方法用于生成一对密钥对，并将该密钥对返回
     *
     * @return 生成的密钥对
     */
    public KeyPair generatekey() {
        try {
            //密钥生成器，指定生成加密算法的密钥
            KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
            //256k1、secp256r1
            ECGenParameterSpec spec = new ECGenParameterSpec("secp256k1");
            generator.initialize(spec); //初始化
            return generator.generateKeyPair();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 该方法用于提供的keypair密钥信息来推导原始格式地址
     *
     * @param keyPair 密钥信息
     * @return 生成的原始格式地址
     */
    public String newAdderss(KeyPair keyPair) {
        ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();

        //自己进行原始格式的拼接
        byte[] pubkey = toUnCompressPub(publicKey);

        //对公钥进行哈希计算
        byte[] sha256 = Hash.SHA256(pubkey);

        //RipeMD160
        byte[] pubhash = Hash.RipeMD160(sha256);

        //版本号
        byte[] version = new byte[1];
        version[0] = 0x00;

        //添加版本号
        byte[] ver_pubhash = new byte[version.length + pubhash.length];
        System.arraycopy(version, 0, ver_pubhash, 0, version.length);
        System.arraycopy(pubhash, 0, ver_pubhash, 1, pubhash.length);

        //双哈希，取前四个字节
        byte[] hash1 = Hash.SHA256(ver_pubhash);
        byte[] hash2 = Hash.SHA256(hash1);

        //校验数组
        byte[] check = new byte[4];
        System.arraycopy(hash2, 0, check, 0, check.length);

        //申请ver_pubhash_check 数组
        byte[] ver_pubhash_check = new byte[ver_pubhash.length + check.length];
        System.arraycopy(ver_pubhash, 0, ver_pubhash_check, 0, ver_pubhash.length);
        System.arraycopy(check, 0, ver_pubhash_check, ver_pubhash.length, check.length);

        //base58编码
        return Base58.encode(ver_pubhash_check);
    }

    /**
     * 用于校验地址是否符合规范，并返回校验结果
     *
     * @param addr 要校验的地址
     * @return 校验结果true表示符合规范，false表示不符合规范
     */
    public boolean isValidate(String addr) {
        if ("".equals(addr) || addr == null) {
            return false;
        }

        //1、base58解码
        byte[] ver_pubhash_check = Base58.decode(addr);
        if (ver_pubhash_check.length < 4) {
            return false;
        }

        //2、申请存储校验位的存储空间
        byte[] check = new byte[4];
        System.arraycopy(ver_pubhash_check, ver_pubhash_check.length - 4, check, 0, check.length);
        byte[] ver_pubhash = new byte[ver_pubhash_check.length - check.length];
        System.arraycopy(ver_pubhash_check, 0, ver_pubhash, 0, ver_pubhash.length);

        //双哈希
        byte[] hash1 = Hash.SHA256(ver_pubhash);
        byte[] hash2 = Hash.SHA256(hash1);
        byte[] check_sum = new byte[4];
        System.arraycopy(hash2, 0, check_sum, 0, check_sum.length);

        return Arrays.equals(check, check_sum);
    }

    /**
     * 该方法用于拼接得到原始格式的公钥
     *
     * @return 原始公钥的字节数组
     */
    public byte[] toUnCompressPub(ECPublicKey publicKey) {
        ECPoint point = publicKey.getW();
        byte[] xbytes = point.getAffineX().toByteArray();
        byte[] ybytes = point.getAffineY().toByteArray();

        //申请65字节大小的字节数组
        //告诉你：04 + x坐标（32） + y坐标（32）
        //公钥数据：原始格式的公钥：04 + x坐标（32） + y坐标（32）
        //x坐标 32字节 ;  33字节  0不要截取[1,33)
        //y坐标 32字节 ;  33字节  0不要截取[1,33)
        byte[] pubkey = new byte[1 + 2 * 32];
        pubkey[0] = 04;

        //X坐标
        if (xbytes.length == 32) {
            System.arraycopy(xbytes, 0, pubkey, 1, xbytes.length);
        } else if (xbytes.length > 32) {
            System.arraycopy(xbytes, 1, pubkey, 1, 32);
        }

        //Y坐标
        if (ybytes.length == 32) {
            System.arraycopy(ybytes, 0, pubkey, 33, ybytes.length);
        } else if (ybytes.length > 32) {
            System.arraycopy(ybytes, 1, pubkey, 33, 32);
        }

        //将65字节的数据返回
        return pubkey;
    }

    //测试用main
    public static void main(String[] args) {
        Address addr = new Address();
        KeyPair keyPair = addr.generatekey();
        String addrStr = addr.newAdderss(keyPair);
        System.out.println(addrStr);

    }





}
